#include "mesh.hh"

#include <sstream>
#include <cstdlib>
#include <cassert>

Mesh* Mesh::m = 0;

xmlNodePtr skipText (xmlNodePtr cur)
{
  while (cur && !xmlStrcmp (cur->name, (const xmlChar*) "text"))
    return cur->next;
  return cur;
}

void myAssert (xmlNodePtr cur, const char* val)
{
  const xmlChar* val2 = (const xmlChar*) val;
  if (xmlStrcmp (cur->name, val2))
  {
    std::cout << "Error wrong balise: " << cur->name << " instead of " << val << std::endl;
    assert (0);
  }
}

void
Mesh::build_vbo (xmlNodePtr cur, xmlDocPtr doc)
{
  //get to the source level
  cur = skipText (cur);
  myAssert (cur, "source");
  cur = cur->xmlChildrenNode;
  assert (cur);

  //get to the float_array  level
  cur = skipText (cur);
  myAssert (cur, "float_array");
  assert (cur);

  int count = atoi ((char*) xmlGetProp (cur, (xmlChar*) "count"));
  count /= 3;

  //get to the array:
  cur = cur->xmlChildrenNode;
  myAssert (cur, "text");

  std::string data ((char*) xmlNodeListGetString (doc, cur, 1));

  //We have everything we want, we can now build our vbo:
  data_ = new Posf[count];
  datasize_ = count;

  std::stringstream ss (data);
  Posf p;
  for (int i = 0; i < count; ++i)
  {
    ss >> p.x;
    ss >> p.y;
    ss >> p.z;
    data_[i] = p;
  }

  //generating the VBO
  glGenBuffersARB (1, &vbo_);
  glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo_);
  glBufferDataARB (GL_ARRAY_BUFFER_ARB, count * sizeof (Posf), data_, GL_STATIC_DRAW_ARB);
}

void
Mesh::build_ibo (xmlNodePtr cur, xmlDocPtr doc)
{
  (void) doc;

  //get the number of triangles
  int count = atoi ((char*) xmlGetProp (cur, (xmlChar*) "count"));

  //go to the children
  cur = cur->xmlChildrenNode;
  assert (cur);
  cur = skipText (cur);

  //count the number of properties, it gives us the value of the modulus
  int nbprop = 0;
  do
  {
    cur = cur->next;
    cur = skipText (cur);
    ++nbprop;
  }
  while (cur && !xmlStrcmp (cur->name, (const xmlChar*) "input"));

  myAssert (cur, "p");
  cur = cur->xmlChildrenNode;
  myAssert (cur, "text");
  std::string data ((char*) xmlNodeListGetString (doc, cur, 1));

  //we have all the data, we can build our ibo
  idatasize_ = 3 * count;
  idata_ = new uint[3 * count];

  //we fill the buffer
  std::stringstream ss (data);
  int tmp;
  int j = 0;
  for (int i = 0; i < 3 * nbprop * count; ++i)
  {
    ss >> tmp;
    if (i % nbprop == 0)
    {
      idata_[j] = tmp;
      ++j;
    }
  }

  //generating the ibo
  glGenBuffersARB (1, &ibo_);
  glBindBufferARB (GL_ELEMENT_ARRAY_BUFFER_ARB, ibo_);
  glBufferDataARB (GL_ELEMENT_ARRAY_BUFFER_ARB, idatasize_ * sizeof (uint), idata_, GL_STATIC_DRAW_ARB);
}

Mesh::Mesh (const Vector3f& pos, const Vector3d& rotate, Bounding* bounding, Mesh* m):
  Object (pos, rotate, bounding),
  vbo_ (m->vbo_),
  data_ (m->data_),
  datasize_ (m->datasize_),
  ibo_ (m->ibo_),
  idata_ (m->idata_),
  idatasize_ (m->idatasize_)
{
}

Mesh::Mesh (const std::string& path)
{
  xmlDocPtr doc;
  
  doc = xmlParseFile (path.c_str ());

  if (!doc)
  {
    std::cout << "couldn't find mesh file: " << path << std::endl;
    exit (1);
  }

  xmlNodePtr cur = xmlDocGetRootElement (doc);

  assert (cur);

  cur = cur->xmlChildrenNode;

  while (cur && xmlStrcmp (cur->name, (const xmlChar*) "library_geometries"))
    cur = cur->next;

  if (!cur)
  {
    std::cout << "Cannot load mesh, could not find geometry information" << std::endl;
    exit (1);
  }

  cur = cur->xmlChildrenNode;

  //get to the geometry level
  cur = skipText (cur);
  myAssert (cur, "geometry");
  cur = cur->xmlChildrenNode;
  assert (cur);

  //get to the mesh level
  cur = skipText (cur);
  myAssert (cur, "mesh");
  cur = cur->xmlChildrenNode;
  assert (cur);

  //we now are at the mesh level
  cur = skipText (cur);
  build_vbo (cur, doc);

  while (cur && xmlStrcmp (cur->name, (const xmlChar*) "triangles"))
    cur = cur->next;

  if (!cur)
  {
    std::cout << "couldn't find triangle section in DAE file, exiting..." << std::endl;
    exit (1);
  }

  build_ibo (cur, doc);

  xmlFreeDoc (doc);
}

// void
// Mesh::dispatch () const
// {
//   glPushMatrix ();
//   glTranslated (this->pos_ (2), this->pos_ (1), this->pos_ (0));

//   glEnableClientState (GL_VERTEX_ARRAY);
//   glColor3ub (255, 0, 0);
//   glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo_);
  
//   glVertexPointer (3, GL_FLOAT, 0, 0);
//   glDrawArrays (GL_POINTS, 0, datasize_);

//   glDisableClientState (GL_VERTEX_ARRAY);
//   glPopMatrix ();
// }

void
Mesh::dispatch () const
{
  glPushMatrix ();

  glTranslated (this->pos_ (2), this->pos_ (1), this->pos_ (0));
  glRotated (this->rotate_ (0), 0, 0, 1);
  glRotated (this->rotate_ (1), 0, 1, 0);
  glRotated (this->rotate_ (2), 1, 0, 0);

  glEnableClientState (GL_VERTEX_ARRAY);

  if (selected_)
    glColor3ub (120, 10, 200);
  else
    glColor3ub (255, 0, 0);

  glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo_);
  
  glVertexPointer (3, GL_FLOAT, 0, 0);
  glBindBufferARB (GL_ELEMENT_ARRAY_BUFFER_ARB, ibo_);
  glDrawElements (GL_TRIANGLES, idatasize_, GL_UNSIGNED_INT, 0);

  glDisableClientState (GL_VERTEX_ARRAY);
  glPopMatrix ();
}
